Selector 选择器
选择器的使命就是完成 IO 多路复用,通过选择器可以同时监控多个通道的 IO 事件。选择器与通道的关系就是监控和被监控的关系
基于之前对 IO 多路复用 的理解我们可以知道,一条线程处理一个选择器,而一个选择器可以监控很多的通道。所以这意味着通过选择器一条线程可以处理成百上千的通道,大量的减少了线程之间上下文切换的开销。
通道和选择器之间通过 register (注册) 的方式完成,调用通道的 Channel.register(Selector sel, int ops)
方法,可以将通道实例注册到一个选择器中。第一个参数指定选择器实例,第二个参数指定 IO 事件类型。
事件类型的定义在 SelectionKey 类中。如果选择器要监控通道的多种时间,可以用 " 按位或 " 运算符来实现。
//监控通道的多种事件,用“按位或”运算符来实现
int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE ;
这里的 IO 事件指的不是对通道的 IO 操作,而是通道的某个 IO 操作的一种就绪状态,比如 SocketChannel 通道, 完成握手连接则处于 " 连接就绪状态 " (OP_CONNECT
)
一个通道是否能被选择,首先要看是否继承 SelectableChannel
类,如果继承了就可以被选择,否则不能。像 FileChannel
就没有继承 SelectableChannel
,所以不可以选择的通道。
SelectionKey 选择键
一旦在通道中发生了某些 IO
事件(就绪状态达成),并且是在选择器中注册过的 IO
事件,就会被选择器选中,并放入 SelectionKey
选择键的集合中。
SelectionKey
选择键就是那些被选择器选中的 IO
事件,那些没有被注册过的通道即使发生了 IO
事件也不会被选择器选中放入选择键集合中
通过选择键不仅仅可获得通道的 IO 事件类型,也可以获得 IO 事件所在的通道,还有选出选择键的选择器实例